package com.arpa.ntocc.common.common.util;

import com.arpa.ntocc.common.common.constant.StrConsts;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisException;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Set;

/**
 * Redis 工具类
 * @Author liuyj
 * @Date 2019-11-18 08:35:32
 */
@Component
@Log4j2
public class RedisUtil {

	private final static String EXCEPTION_IS_GETTING_REDIS_EMPTY = "异常：获取redis为空";

	/**
	 * Redis服务器IP
 	 */
	private static String ip;

	/**
	 * Redis的端口号
 	 */
	private static int port;

	/**
	 * 访问密码
	 */
	private static String password;
	/**
	 * 可用连接实例的最大数目，默认值为8；
	 * 如果赋值为-1，则表示不限制；如果pool已经分配了maxActive个jedis实例，则此时pool的状态为exhausted(耗尽)。
	 */
	private static int maxActive;

	/**
	 * 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例，默认值是8。
	 */
	private static int maxIdle;

	/**
	 * 控制一个pool最少有多少个状态为idle(空闲的)的jedis实例，默认值是0。
	 */
	private static int minIdle;

	/**
	 * 等待可用连接的最大时间，单位毫秒，默认值为-1，表示永不超时。如果超过等待时间，则直接抛出JedisConnectionException；
	 */
	private static int maxWait;

	/**
	 * 超时时间
	 */
	private static int timeout;

	/**
	 * redis 使用库的编号
	 */
	private static int databaseName;

	/**
	 * 在borrow一个jedis实例时，是否提前进行validate操作；如果为true，则得到的jedis实例均是可用的；
	 */
	private static final boolean TEST_ON_BORROW = false;

	/**
	 * jedis池
	 */
	private static JedisPool jedisPool = null;

	/**
	 * 初始化jedisPool
	 */
	@PostConstruct
	public void initJedisPool() {
		try {
			JedisPoolConfig config = new JedisPoolConfig();
			config.setMaxTotal(maxActive);
			config.setMaxIdle(maxIdle);
			config.setMinIdle(minIdle);
			config.setMaxWaitMillis(maxWait);
			config.setTestOnBorrow(TEST_ON_BORROW);
			RedisUtil.jedisPool = new JedisPool(config, ip, port, timeout, password, databaseName);
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		}
	}

	/**
	 * 同步获取Jedis实例
	 * @return Jedis
	 */
	public  static synchronized  Jedis getJedis() {
		Jedis jedis = null;
		try {
			if (null != jedisPool) {
				jedis = jedisPool.getResource();
			}
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		}
		return jedis;
	}

	/**
	 * 设置 String
	 * @param key
	 */
	public static synchronized  boolean exists(String key) {
		if(StringUtils.isBlank(key)){
			return false;
		}
		boolean exists=false;
		try {
			Jedis jedis = getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			exists=jedis.exists(key);
			jedis.close();
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		}
		return exists;
	}
	/**
	 * 设置 String
	 * @param key
	 * @param value
	 */
	public static synchronized  void set(String key, String value) {
		try {
			value = StringUtils.isBlank(value) ? "" : value;
			Jedis jedis = getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			jedis.set(key, value);
			jedis.close();
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		}
	}

	/**
	 * 设置 byte[]
	 * @param key
	 * @param value
	 */
	public static synchronized  void set(byte[] key, byte[] value) {
		try {
			Jedis jedis = getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			jedis.set(key, value);
			jedis.close();
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		}
	}

	/**
	 * 设置 String 过期时间
	 * @param key
	 * @param value
	 * @param seconds 以秒为单位
	 */
	public static synchronized  void set(String key, String value, int seconds) {
		try {
			value = StringUtils.isBlank(value) ? "" : value;
			Jedis jedis = getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			jedis.setex(key, seconds, value);
			jedis.close();
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		}
	}

	/**
	 * 设置 String 过期时间
	 * @param key
	 * @param value
	 * @param millisecond 以毫秒为单位
	 */
	public static synchronized  void setPexpire(String key, String value, int millisecond) {
		try {
			value = StringUtils.isBlank(value) ? "" : value;
			Jedis jedis = getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			jedis.set(key, value);
			jedis.pexpire(key,millisecond);
			jedis.close();
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		}
	}

	/**
	 * 设置 byte[] 过期时间
	 * @param key
	 * @param value
	 * @param seconds 以秒为单位
	 */
	public static synchronized  void set(byte[] key, byte[] value, int seconds) {
		try {
			Jedis jedis = getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			jedis.set(key, value);
			jedis.expire(key, seconds);
			jedis.close();
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		}
	}

	/**
	 * 获取String值
	 * @param key
	 * @return value
	 */
	public static String get(String key) {
		Jedis jedis = getJedis();
		if (null == jedis) {
			return null;
		}
		String value = jedis.get(key);
		jedis.close();
		return value;
	}

	/**
	 * 获取byte[]值
	 * @param key
	 * @return value
	 */
	public static byte[] get(byte[] key) {
		Jedis jedis = getJedis();
		if (null == jedis) {
			return null;
		}
		byte[] value = jedis.get(key);
		jedis.close();
		return value;
	}

	/**
	 * 删除值
	 * @param key
	 */
	public static synchronized   void remove(String key) {
		try {
			Jedis jedis = getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			jedis.del(key);
			jedis.close();
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		}
	}

	/**
	 * 删除值
	 * @param key
	 */
	public static synchronized  void remove(byte[] key) {
		try {
			Jedis jedis = getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			jedis.del(key);
			jedis.close();
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		}
	}

	/**
	 * lpush
	 * @param key
	 * @param key
	 */
	public  static synchronized  void lpush(String key, String... strings) {
		try {
			Jedis jedis = RedisUtil.getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			Long afterPushSize = jedis.lpush(key, strings);
			jedis.close();
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		}
	}

	/**
	 * 获取key这个List，从第几个元素到第几个元素 LRANGE key start
	 * stop返回列表key中指定区间内的元素，区间以偏移量start和stop指定。
	 * 下标(index)参数start和stop都以0为底，也就是说，以0表示列表的第一个元素，以1表示列表的第二个元素，以此类推。
	 * 也可以使用负数下标，以-1表示列表的最后一个元素，-2表示列表的倒数第二个元素，以此类推。
	 */
	public static List<String> lrange(String key, long start, long end) {
		Jedis jedis = null;
		List<String> ret=null;
		try {
			jedis = getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			ret = jedis.lrange(key, start, end);

		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
		return ret;
	}

	/**
	 * 对一个列表进行修剪(trim)，就是说，让列表只保留指定区间内的元素，不在指定区间之内的元素都将被删除。
	 * 下标 0 表示列表的第一个元素，以 1 表示列表的第二个元素，以此类推。
	 * 你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推
	 * 小技巧：ltrim mylist 1 0 用于清空mylist列表（因为只要start大于end 就没有符合条件的）
	 */
	public static void ltrim(String key, long start, long end) {
		Jedis jedis = null;
		try {
			jedis = getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			jedis.ltrim(key, start, end);
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	/**
	 * lrem
	 * @param key
	 * @param count
	 * @param value
	 * count > 0 : 从表头开始向表尾搜索，移除与 VALUE 相等的元素，数量为 COUNT 。
	 * count < 0 : 从表尾开始向表头搜索，移除与 VALUE 相等的元素，数量为 COUNT 的绝对值。
	 * count = 0 : 移除表中所有与 VALUE 相等的值。
	 */
	public static synchronized  void lrem(String key, long count, String value) {
		try {
			Jedis jedis = RedisUtil.getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			jedis.lrem(key, count, value);
			jedis.close();
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		}
	}

	/**
	 * sadd
	 * @param key
	 * @param value
	 * @param seconds
	 */
	public  static synchronized  void sadd(String key, String value, int seconds) {
		try {
			Jedis jedis = RedisUtil.getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			jedis.sadd(key, value);
			jedis.expire(key, seconds);
			jedis.close();
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
		}
	}
	/**
	 * sismember  key对应的set 中是否含有Value
	 * @param key
	 * @param value
	 */
	public static synchronized  boolean sismember(String key, String value) {
		try {
			Jedis jedis = RedisUtil.getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			boolean contain= jedis.sismember(key, value);
			jedis.close();
			return contain;
		} catch (Exception e) {
			log.error(StrConsts.CATCH_MESSAGE,e);
			return false;
		}
	}

	/**
	 * incr
	 * @param key
	 * @return value
	 */
	public static synchronized  Long incr(String key) {
		Jedis jedis = getJedis();
		if (null == jedis) {
			return null;
		}
		long value = jedis.incr(key);
		jedis.close();
		return value;
	}

	/**
	 * incr
	 * @param key
	 * @return value
	 */
	public static synchronized  Long incrby(String key,int count) {
		Jedis jedis = getJedis();
		if (null == jedis) {
			return null;
		}
		long value = jedis.incrBy(key,count);
		jedis.close();
		return value;
	}

	/**
	 * decr
	 * @param key
	 * @return value
	 */
	public static synchronized  Long decr(String key) {
		Jedis jedis = getJedis();
		if (null == jedis) {
			return null;
		}
		long value = jedis.decr(key);
		jedis.close();
		return value;
	}
	/**
	 * 返回匹配的 keys 列表
	 */
	public static Set<String> keys(String pattern) {
		Jedis jedis = null;
		try {
			jedis = getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			Set<String> keys = jedis.keys(pattern);
			return keys;
		} catch (Exception e) {
			throw new JedisException(e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}
	/**
	 * 为给定 key 设置过期时间。
	 */
	public  static 	void expire(String key, int seconds) {
		Jedis jedis = null;
		try {
			jedis = getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			jedis.expire(key, seconds);
		} catch (Exception e) {
			throw new JedisException(e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	/**
	 * 以秒为单位，返回给定 key 的剩余生存时间
	 */
	public static long ttl(String key) {
		Jedis jedis = null;
		try {
			jedis = getJedis();
			if(null==jedis){
				throw  new Exception(EXCEPTION_IS_GETTING_REDIS_EMPTY);
			}
			return jedis.ttl(key);
		} catch (Exception e) {
			throw new JedisException(e);
		} finally {
			if (jedis != null) {
				jedis.close();
			}
		}
	}

	@Value(value="${spring.redis.host}")
	public void setIP(String IP) {
		RedisUtil.ip = IP;
	}

	@Value(value="${spring.redis.port}")
	public void setPORT(int PORT) {
		RedisUtil.port = PORT;
	}

	@Value(value="${spring.redis.password}")
	public void setPASSWORD(String PASSWORD) {
		RedisUtil.password = PASSWORD;
	}

	@Value(value="${spring.redis.database}")
	public void setOtherDatabaseName(int databaseName) {
		RedisUtil.databaseName = databaseName;
	}

	@Value(value="${spring.redis.jedis.pool.max-active}")
	public void setMaxActive(int maxActive) {
		RedisUtil.maxActive = maxActive;
	}

	@Value(value="${spring.redis.jedis.pool.max-idle}")
	public void setMaxIdle(int maxIdle) {
		RedisUtil.maxIdle = maxIdle;
	}

	@Value(value="${spring.redis.jedis.pool.min-idle}")
	public void setMinIdle(int minIdle) {
		RedisUtil.minIdle = minIdle;
	}

	@Value(value="${spring.redis.jedis.pool.max-wait}")
	public void setMaxWait(int maxWait) {
		RedisUtil.maxWait = maxWait;
	}

	@Value(value="${spring.redis.timeout}")
	public void setTimeout(int timeout) {
		RedisUtil.timeout = timeout;
	}

}